home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Light ROM Gold
/
Light ROM Gold.iso
/
arexx
/
modeler
/
hair.lwm
< prev
next >
Wrap
Text File
|
1995-03-23
|
11KB
|
385 lines
/* CMD: Random Surface Points
* and Lines
* Generate a set of points randomly distributed across the surface of
* an object by pricking it with many tiny needles.
*
* Redone in 08/94 to create hair by Gonzalo Garramuo
* To create Hair: cut all the polygons of your head that should have
* hair to a new layer and run this program. To create
* realistic hair you should use pretty high numbers
* (2000-10000) in the Number of Points Requester.
* With this high numbers, it can take a while.
* After the macro finishes, you may still want to
* retouch the hair a little bit, to avoid it to go
* through the ears of your character, to "comb" it, etc.
* The "hair" are made of a huge number of lines (2-point
* particles or polygons), therefore, you should use
* shadows when rendering. Why? Because these 2-
* sided polygons will receive light a little different
* than normal polygons: they won't be shaded when they
* should be.
* version 2.1 10/94 by Gonzalo Garramuo
* added segmentation to lines (to allow "combing")
* version 2.2 10/94 by Gonzalo Garramuo
* added rotation center and sizing center
*/
syscode = "Random Surface Points"
statfil = 'T:RandomPoints.state'
version = 'Prick v2.2'
/* Boilerplate.*/
mxx = "LWModelerARexx.port"
mxx_add = addlib(mxx,0)
signal on error
signal on syntax
call addlib("rexxmathlib.library",0,-30,0)
lines = 0
call main
if (mxx_add) then call remlib(mxx)
exit
syntax:
error:
t = Notify(1,'!Rexx Script Error','@'ErrorText(rc),'Line 'SIGL)
if (mxx_add) then call remlib(mxx)
exit
main:
fg = curlayer()
emp = emptylayers()
if (words(emp) < 2) then do
call notify(1,"!Need two background layers for scratch work.")
return
end
temp = word(emp,1)
temp2 = word(emp,2)
/* Get bounding box for fg data. */
parse value boundingbox() with n x1 x2 y1 y2 z1 z2 .
if (n < 3) then do
call notify(1,"!I need at least a 3-sided polygon to work on.")
return
end
center = (x1+x2)/2 (y1+y2)/2 (z1+z2)/2
if (setup() = 0) then return
lo = x1 y1 z1
hi = x2 y2 z2
dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
/* Compute our slightly expanded box for the ends of the needles. */
exb = max(dx, dy, dz) / 20
xx1 = x1 - exb
xx2 = x2 + exb
yy1 = y1 - exb
yy2 = y2 + exb
zz1 = z1 - exb
zz2 = z2 + exb
/* Compute distribution cell size. We want about "num" points
* distributed along the surface of the bounding box, so take that
* surface area, divide by num. Square root of that is the cell
* edge size (s). Also compute min allowed distance (md) and
* tiny delta for needle construction (d).
*/
sa = 2 * (dx * dy + dy * dz + dz * dx)
s = sqrt(sa / num)
md = s / 4
d = md / 20
say "Cell edge size:" s " Merge dist:" md
/* Goto background layer and build our lattice of needles. */
call setlayer(temp)
call add_begin()
nx = dx % s
ny = dy % s
nz = dz % s
say "Needle grid" nx ny nz
call meter_begin(nx * ny + nx * nz + ny * nz, syscode)
do xi = 1 to nx
x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
do yi = 1 to ny
y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
xa = x0 + (randu()-0.5) * s
xb = x0 + (randu()-0.5) * s
ya = y0 + (randu()-0.5) * s
yb = y0 + (randu()-0.5) * s
i1 = add_point(xa ya zz1)
i2 = add_point(xa ya+d zz1)
i3 = add_point(xb yb zz2)
call add_polygon(i1 i2 i3)
call meter_step()
end yi
end xi
do xi = 1 to nx
x0 = x1 + (x2 - x1) / nx * (xi - 0.5)
do zi = 1 to nz
z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
xa = x0 + (randu()-0.5) * s
xb = x0 + (randu()-0.5) * s
za = z0 + (randu()-0.5) * s
zb = z0 + (randu()-0.5) * s
i1 = add_point(xa yy1 za)
i2 = add_point(xa yy1 za+d)
i3 = add_point(xb yy2 zb)
call add_polygon(i1 i2 i3)
call meter_step()
end zi
end xi
do yi = 1 to ny
y0 = y1 + (y2 - y1) / ny * (yi - 0.5)
do zi = 1 to nz
z0 = z1 + (z2 - z1) / nz * (zi - 0.5)
ya = y0 + (randu()-0.5) * s
yb = y0 + (randu()-0.5) * s
za = z0 + (randu()-0.5) * s
zb = z0 + (randu()-0.5) * s
i1 = add_point(xx1 ya za)
i2 = add_point(xx1 ya za+d)
i3 = add_point(xx2 yb zb)
call add_polygon(i1 i2 i3)
call meter_step()
end zi
end yi
call meter_end()
call add_end()
/* Perform the slice and leave only the points. */
call setblayer(fg)
call soliddrill(SLICE)
call removepols()
/* Delete the points that lie outside the bounding box of the
* original object, leaving the ones that intersected the interior
* surfaces. Then merge points using our min distance. */
call sel_mode(USER)
call sel_point(SET)
d = exb / 2
call sel_point(CLEAR, VOL, x1-d y1-d z1-d, x2+d y2+d z2+d)
call cut()
call mergepoints(md)
if (lines) then call hair
if same = 1 then call CUT()
call SETLAYER(temp2)
call CUT()
if same = 1 then do
call SETLAYER(fg)
end
call PASTE()
return
setup:
/* Setup state variables, reading stored ones, if any. */
num = 500
lines = 1
size = 1.05
rot = 0
rotcenter = center
sizcenter= center
segments = 2
axis = 1
jitter = ((x1-word(center,1))/10) ((y1-word(center,2))/10)
jitter = jitter||((z1-word(center,3))/10)
jittertype = 1
same = 1
if (exists(statfil)) then do
if (~open(state, statfil, 'R')) then break
if (readln(state) ~= version) then break
num = readln(state)
lines = readln(state)
segments = readln(state)
size = readln(state)
sizcenter = readln(state)
rot = readln(state)
rotcenter = readln(state)
axis = readln(state)
jitter = readln(state)
jittertype = readln(state)
same = readln(state)
call close state
end
/* Query user for their function and area to evaluate. */
call req_begin syscode
id_num = req_addcontrol("Max. Number of Points", 'N')
id_lines= req_addcontrol("Create lines", 'B')
id_segments = req_addcontrol("Number of Segments", 'N')
id_size= req_addcontrol("Line Size (Scaling)", 'N')
id_sizcenter= req_addcontrol("Sizing Center","V",1)
id_rot= req_addcontrol("Line Angle (Rotation)", 'N')
id_rotcenter= req_addcontrol("Rotation Center", 'V', 1)
id_axis= req_addcontrol("Rotation Axis", C, "X Y Z")
id_jitter= req_addcontrol("Jitter Amount", V, 1)
id_jittertype= req_addcontrol("Jitter Type", C, "Uniform Gaussian
Normal Radial")
id_same= req_addcontrol("Put Results on Original Layer", 'B')
call req_setval id_num, num
call req_setval id_lines, lines
call req_setval id_segments, segments
call req_setval id_size, size
call req_setval id_sizcenter, sizcenter
call req_setval id_rot, rot
call req_setval id_rotcenter, rotcenter
call req_setval id_axis, axis
call req_setval id_jitter, jitter
call req_setval id_jittertype, jittertype
call req_setval id_same, same
if (~req_post()) then do
call req_end
return 0
end
num = req_getval(id_num) % 1
lines = req_getval(id_lines)
segments = req_getval(id_segments)
size = req_getval(id_size)
sizcenter = req_getval(id_sizcenter)
rot = req_getval(id_rot)
rotcenter = req_getval(id_rotcenter)
axis = req_getval(id_axis)
jitter= req_getval(id_jitter)
jittertype = req_getval(id_jittertype)
same = req_getval(id_same)
call req_end
/* Save state now, in case something fails. */
if (open(state, statfil, 'W')) then do
call writeln state, version
call writeln state, num
call writeln state, lines
call writeln state, segments
call writeln state, size
call writeln state, sizcenter
call writeln state, rot
call writeln state, rotcenter
call writeln state, axis
call writeln state, jitter
call writeln state, jittertype
call writeln state, same
call close state
end
return 1
/* How does it work? It copies all the points created by prick, sizes
them, rotates them, and jitters them. Finally it draws a line
connecting each of the old points with the new, modified copies of
themselves. If you use good values, the results are good hair. */
hair:
if size >= 1 then do
sizepreset = (size - 1) / segments
end
else do
sizepreset = -((1 - size) / segments)
end
size = 1 + sizepreset
if rot > 0 then do
rot = rot / segments
end
do l = 1 to segments
call COPY()
call SCALE(size size size,sizcenter)
IF rot~=0 then do
call ROTATE(rot,translate(axis,"XYZ","123"),rotcenter)
end
select
when jittertype=1 then jittertype="Gaussian"
when jittertype=2 then jittertype="Uniform"
when jittertype=3 then jittertype="Normal"
otherwise jittertype="Radial"
end
/* Lightwave 3.0-3.1 users, type "call JITTER(jitter)" instead of
next line. */
call JITTER(jitter,jittertype)
call PASTE()
n = xfrm_begin()
if n=0 then return
/* The meters can get a little corrupted, but it is okay */
call meter_begin n, 'Drawing 'n/2' Lines', "Step 1"
do i = 1 to n
Point.i= xfrm_getpos(i)
call meter_step
end
call xfrm_end()
call CUT()
call meter_end()
/* Modeler works very strangely here. It seems the COPY() and PASTE()
also copy the point order. So that when you paste, let's say, point #2
and there is already point #2 in that layer, it makes the new point
point #3, and the old point number #3 becomes #4 (ie. it inserts
points in the middle of the points already in the layer).
If COPY() and PASTE() worked as it should be expected, so that if you
have 300 points in the layer, and copy other 200 to it, these new 200
points would go from 301-500, then this loop would be different. It
would go from 1 to n/2 and the second point added would be i=i+n,
insted of i=i+1. Weird! It took me three hours to figure this out.
This is not mentioned in the Arexx docs. */
call meter_begin n/2, 'Drawing 'n/2' Lines', "Step 2"
call add_begin()
s=0
do i=1 to n
s=s+1
call add_point Point.i
i=i+1
call add_point Point.i
s=s+1
call add_polygon s-1 s
call meter_step
end i
call add_end()
call meter_end()
call CUT()
call setlayer(temp2)
call PASTE()
call MERGEPOINTS()
call setlayer(temp)
call add_begin()
do i=1 to n-1
i=i+1
call add_point Point.i
end
call add_end()
end l
return
/* End of Macro */